package vcp.client.GUI.mvc.controller;

import java.awt.BorderLayout;

import org.joda.time.DateTime;

import vcp.client.GUI.factories.MVCFactory;
import vcp.client.GUI.mvc.model.UIAbstractModel;
import vcp.client.GUI.mvc.model.UILoginModel;
import vcp.client.GUI.mvc.view.UIAbstractView;
import vcp.client.GUI.mvc.view.UISubRootView;
import vcp.client.GUI.mvc.view.addons.LeftNavigation;
import vcp.client.exceptions.ReplaceMVCException;
import vcp.client.interfaces.delegates.IParentControllerDelegate;
import vcp.client.utility.Common.UserNotification;
import vcp.entity.User;
import vcp.enums.PopUpMessageType;
import vcp.enums.Screen;

/**
 * 
 * @author VCP team
 *         <p>
 *         a sub root controller class, responsible for managing logged users
 *         systems with navigations tabs
 *         </p>
 * 
 * @see IParentControllerDelegate
 */
public class UISubRootController extends UIAbstractController implements IParentControllerDelegate
{
	/**
	 * <p>
	 * Responsible to give the sub controller of inner parts the next MVC
	 * pressed by navigation tabs.
	 * </p>
	 */
	protected final MVCFactory factoryController;

	/**
	 * hold current on screen MVC controller
	 */
	protected UIAbstractController currentMVC;

	/**
	 * reference to model
	 * 
	 * responsible for performing logout
	 */
	protected final UILoginModel model;

	/**
	 * responsible for swapping between inner views
	 */
	private final UISubRootView view;

	/** instance of logged in user **/
	protected User user;

	// ****************************************************************************************************************************
	// Getter-Setter methods
	// ****************************************************************************************************************************
	protected synchronized User getUser ()
	{
		return this.user;
	}

	protected synchronized void setUser ( User user )
	{
		this.user = user;
	}

	protected String[] navigationTabs;

	/**
	 * 
	 * @param view
	 *            screen type
	 * 
	 * @param model
	 *            fill with data
	 * 
	 * @param factory
	 *            factory info
	 * 
	 */
	public UISubRootController ( UISubRootView view, UILoginModel model, MVCFactory factory )
	{
		// passes null to super as this sub controller is abstract and will not
		// hold screen name
		super(null);
		this.factoryController = factory;
		this.model = model;
		this.view = view;
		this.setupListeners();
	}

	// ****************************************************************************************************************************
	// Protected methods
	// ****************************************************************************************************************************
	/**
	 * reset all the components of MVC and flashes the data which was stored
	 */
	protected void resetAllMVC ()
	{
		for ( String tabName : this.navigationTabs )
		{
			Screen screenName = Screen.convert(tabName);
			if ( screenName != null )
			{
				try
				{
					UIAbstractController mvc = this.extractMVC(screenName);
					if ( mvc != null )
					{
						mvc.flashData();
					}
				}
				catch ( ReplaceMVCException e )
				{
				}
			}
		}

		this.user = null;
		this.view.removeView(this.currentMVC.getView());
	}

	/**
	 * Tells the sub controller to replace inner MVC
	 * 
	 * @param newMVC
	 *            : The new required MVC
	 */
	protected void replaceMVC ( UIAbstractController newMVC )
	{
		// remove from screen old MVC
		this.removeOldInnerView();

		// stop spiral spinning
		this.loadingStop();

		// replaces the new
		this.currentMVC = newMVC;

		this.addNewInnerView();
	}

	/**
	 * Adding new inner <b><u>view</u></b> to main frame
	 */
	protected void addNewInnerView ()
	{
		this.view.addView(this.currentMVC.getView(), BorderLayout.CENTER);
		this.updateScreenName(this.currentMVC.getScreenName());
	}

	/**
	 * Removing old inner <b><u>view</u></b> to main frame
	 */
	protected void removeOldInnerView ()
	{
		this.view.removeView(this.currentMVC.getView());
	}

	/**
	 * extract all the MVC components
	 * 
	 * @param newScreen
	 * @return created UIAbstractController
	 * @throws ReplaceMVCException
	 */
	protected UIAbstractController extractMVC ( Screen newScreen )
			throws ReplaceMVCException
	{
		UIAbstractController newMVC = this.factoryController.createController(newScreen, this);
		if ( newMVC == null )
		{
			String error = String.format(UserNotification.UNABLE_TO_CREATE__NAME__SCREEN, newScreen.toString());
			throw new ReplaceMVCException(error);
		}

		return newMVC;
	}

	// ****************************************************************************************************************************
	// Private methods
	// ****************************************************************************************************************************
	private LeftNavigation createTabs ( String[] tabs )
	{
		return new LeftNavigation(tabs);
	}

	// ****************************************************************************************************************************
	// UIAbstractController methods
	// ****************************************************************************************************************************
	@Override
	public UIAbstractView getView ()
	{
		return this.view;
	}

	@Override
	public UIAbstractModel getModel ()
	{
		return this.model;
	}

	@Override
	public void uploadDataContext ( Object dataContext )
	{
		if ( dataContext instanceof User )
		{
			this.setUser((User) dataContext);

			// creates navigation tabs
			this.navigationTabs = this.getUser().getVisibleTabs();
			this.view.setLeftNavigationView(this.createTabs(this.navigationTabs));

			// creates inner-inner MVC
			Screen screen = this.getUser().getInitTabScreen();
			this.currentMVC = this.factoryController.createController(screen, this);
			this.view.addView(this.currentMVC.getView(), BorderLayout.CENTER);

			this.currentMVC.uploadDataContext(this.getUser());
		}
	}

	@Override
	public void clearMVC ()
	{
		this.model.performLogout();
		this.resetAllMVC();
		this.view.removeLeftNavigation();
	}

	@Override
	public Screen getScreenName ()
	{
		return this.currentMVC.getScreenName();
	}

	// ****************************************************************************************************************************
	// INotify methods
	// ****************************************************************************************************************************
	@Override
	public void loadMVC ( Screen newScreen )
	{

	}

	@Override
	public void loadMVCWithData ( Screen newScreen, Object dataObject )
	{
		try
		{
			this.currentMVC.clearMVC();
			UIAbstractController newMVC = this.extractMVC(newScreen);
			newMVC.uploadDataContext(this.getUser());
			this.replaceMVC(newMVC);
		}
		catch ( ReplaceMVCException exception )
		{
			this.showPopUpMessage(exception.getMessage(), PopUpMessageType.ERROR);
			this.loadingStop();
		}
	}

	@Override
	public void showPopUpMessage ( String displayMessage, PopUpMessageType messageType )
	{
		this.parentDelegate.showPopUpMessage(displayMessage, messageType);
	}

	@Override
	public void updateScreenName ( Screen name )
	{
		this.parentDelegate.updateScreenName(name);
	}

	// ****************************************************************************************************************************
	// IParentControllerDelegate methods
	// ****************************************************************************************************************************
	@Override
	public void notifyOnDataUpdate ( Object data )
	{
		if ( data instanceof User )
		{
			this.setUser((User) data);
		}
	}

	@Override
	public DateTime requestCurrentClientDateTime ()
	{
		return this.parentDelegate.requestCurrentClientDateTime();
	}

	// ****************************************************************************************************************************
	// IControllerViewDelegate methods
	// ****************************************************************************************************************************
	@Override
	public void onButtonPress ( Object object )
	{
		if ( object != null )
		{
			Screen name = (Screen) object;
			this.loadMVCWithData(name, null);
		}
		else
		{
			this.parentDelegate.loadMVC(Screen.LOGIN);
		}
	}
}
